
from threading import Condition, Thread

from context import Context
from pcb import PCB
from scheduler import Scheduler
from main.model.logic.interruption import Interruption
from construct.model.logic.interruption_manager_builder import InterruptionManagerBuilder
from Queue import Queue


class Kernel(Thread):
    
    def __init__(self, device_manager, os_usher, round_robin_enable=False, idle_lock=None):
        Thread.__init__(self)
        self.__os_usher             = os_usher
        self.__is_stop              = False
        self.__idle_lock            = idle_lock or Condition()
        self.__pcbs                 = []
        self.__interruptions        = Queue()
        self.__interruption_manager = InterruptionManagerBuilder().get_default(self)
        self.__device_manager       = device_manager
        self.__scheduler            = Scheduler(device_manager, round_robin_enable)
        
    def run(self):
        self.scheduler.setup()
        while(not self.is_stop):
            self.handle_interruption()
            self.cpu.run()
    
    def add_interruption(self, interrruption):
        self.usher.set_kernel_mode()
        self.interruptions.put(interrruption)
        self.wake_up_if_idle()
    
    def handle_interruption(self):
        while not self.interruptions.empty():
            self.interruption_manager.handle(self.interruptions.get())
        
        with self.idle_lock:
            if(self.cpu.is_free):
                if(self.scheduler.has_next()):
                    self.load_to_cpu(self.scheduler.next())
                    self.usher.set_user_mode()
                else:
                    self.idle_lock.wait()  
            else:
                self.usher.set_user_mode()

    def wake_up_if_idle(self):
        with self.idle_lock:
            if(self.cpu.is_free):
                self.idle_lock.notify()
        
    def delete_pcb(self, pcb):
        self.output.send(pcb.process_name + " - Finished")
        self.pcbs.remove(pcb)
        self.memory.unlink(pcb)
        
    def send_to_ready(self, pcb):
        self.scheduler.add(pcb)
        
    def send_to_io(self, pcb):
        self.io.add(pcb, 5)
            
    def load_to_cpu(self, pcb):
        self.cpu.context = pcb.context
        self.cpu.work_to_do()
    
    def execute(self, _program_name, _params):
        self.add_interruption(Interruption(Interruption.EXECUTE, None, program_name=_program_name, params=_params))
    
    def load(self, program_name, params):        
        self.output.send("loading \"" + program_name + "\"")
        
        program = self.disk.get_program_by_name(program_name)
        new_pcb = PCB(program_name)
        block =  self.memory.load(program, new_pcb)
        context = Context(block)
        new_pcb.context = context;
        context.pcb = new_pcb
        self.scheduler.add(new_pcb)
        self.pcbs.append(new_pcb)
        
    def stop(self):
        self.usher.set_kernel_mode()
        self.__is_stop = True
        self.cpu.stop()
        with self.idle_lock:
            self.idle_lock.notify_all()  
    

    @property
    def memory(self): return self.device_manager.memory

    @property
    def disk(self): return self.device_manager.disk
    
    @property
    def io(self): return self.device_manager.io
    
    @property
    def cpu(self): return self.device_manager.cpu

    @property
    def device_manager(self): return self.__device_manager
    
    @property
    def scheduler(self): return self.__scheduler

    @property
    def pcbs(self): return self.__pcbs
    
    @property
    def interruptions(self): return self.__interruptions
    
    @property
    def interruption_manager(self): return self.__interruption_manager
    
    @property
    def is_stop(self): return self.__is_stop
    
    @property
    def idle_lock(self): return self.__idle_lock
    
    @property
    def usher(self): return self.__os_usher
    
    @property
    def output(self): return self.device_manager.output

